import Requset from "./request";
import getUUID from "./uuid";
import WxmpRsa from "wxmp-rsa";

export default class BuryingPonit {
  // apiKey
  #key;
  // 系统信息
  #devices;
  // 请求
  #requset;
  // 事件存储
  #eventList = [];
  // 页面时间记录
  #timer = null;
  #timerEnd = null;
  // 整体时长记录
  #pageTimer = null;
  #pageTimerEnd = null;
  // 静默时间记录
  #start_time = 0;
  // 小程序启动时的唯一id
  #zz_launching_id = null;
  // 每50条更换一次id
  #zz_session_id = null;
  // 自定义事件（来源后台）
  #customEvenList = [];
  // 自定义页面（来源后台）
  #pageList = [];
  // 自定义属性（来源后台）
  #superProperties = [];
  // rsa加密
  #RSA = null;
  // 曝光config
  #exposureConfig = {
    // 元素比例0-1
    rate: 0.5,
    // 显示时长
    duration: 2,
    // 元素唯一id
    ids: [],
    // 事件
    event: "$show_event",
  };
  // 页面浏览时间计时
  #screen_time = 10;
  // 曝光元素数组
  #elementList = [];
  #launchFre = 0;

  // 60s上报定时器
  #intervalTim = null;
  constructor({ key, baseUrl, screenTime = 10 }) {
    // 获取详情 appId
    this.#screen_time = screenTime;
    this.#requset = new Requset(baseUrl);
    this.#key = key;
    this.#zz_session_id = getUUID();
    this.#getAppDetail({ appId: this.#key });
    this.#RSA = new WxmpRsa();
    // 初始化
    this.init();
  }

  // 初始化基本能力
  async init() {
    // 初始化
    this.#setPage();
    await this.#getSysInfo();
    this.#onLaunch();
    this.#onShow();
    this.#getAppletInit();
    this.#intervalSend();
  }

  // 设置系统信息
  async #getSysInfo() {
    let sysInfo = wx.getSystemInfoSync();
    let { miniProgram } = wx.getAccountInfoSync();
    let res = await wx.getNetworkType();
    let brand = sysInfo.system.split(" ");
    this.#zz_launching_id = getUUID();
    this.#devices = {
      // 屏幕高
      $screen_height: sysInfo.screenHeight,
      // 屏幕宽
      $screen_width: sysInfo.screenWidth,
      // 系统版本
      $os_version:
        Array.isArray(brand) && brand?.length ? brand[brand?.length - 1] : "",
      // 系统
      $os: Array.isArray(brand) && brand?.length ? brand[0] : "",
      // 小程序版本
      $app_version_string: miniProgram.version,
      // 设备型号
      $model: sysInfo.model || "",
      // 品牌
      $brand: sysInfo.brand || "",
      // 是否wifi
      $wifi: res?.networkType == "wifi" ? "1" : "",
      // 用户id 不手动设置是空的
      $distinct_id: "",
      // 设备id
      $device_id: getUUID(),
    };
  }

  // 获取应用实例等
  #getAppletInit() {
    // 页面显示
    wx.onAppShow(() => {
      // console.log("onAppShow");
      if (this.#launchFre) {
        this.#onShow();
      }
    });
    // 页面隐藏
    wx.onAppHide(() => {
      this.#onHide();
    });
  }

  // 时间记录
  trackTime() {
    this.#timer = Date.now();
  }
  // 时间结束
  trackTimeEnd({ event, ...val }) {
    if (!this.#timer) return;
    this.#timerEnd = Date.now();
    let tim = this.#calcTim(this.#timer, this.#timerEnd);
    this.trackEvents({
      properties: {
        $duration: tim,
      },
      event,
      ...val,
    });
    // 重置
    this.#timerEnd = 0;
    this.#timer = 0;
  }

  // 时间计算 简版
  #calcTim(start, end) {
    return (end - start) / 1000;
  }

  // 设置系统信息等
  setDevices(val) {
    this.#devices = {
      ...this.#devices,
      ...val,
    };
  }

  // 判断本次上传事件是否存在于后台之中，不存在不予缓存
  #getEventExist(val) {
    // 预置事件
    let presetEventList = [
      "$ae_first_open",
      "$ae_updated",
      "$ae_app_open",
      "$ae_app_start",
      "$ae_app_terminate",
      "$ae_crashed",
      "$ae_appview_screen",
      "$identify",
      "$show_event",
    ];
    // 是否存在预置事件
    if (
      presetEventList.includes(val?.event) ||
      this.#customEvenList.length == 0
    ) {
      return true;
    }

    // 是否存在事件
    if (!val?.event) return false;
    let { event, properties, ...other } = val;
    let data = this.#customEvenList.find((i) => i.event_name == event);
    // 是否存在参数
    if (
      !data?.event_name ||
      Object.keys(other).length != data.properties.length
    ) {
      return false;
    }
    let properEve = data.properties.map((it) => it.propertyName);
    // 判断key是否存在
    for (let k of properEve) {
      if (!Object.keys(other).includes(k)) {
        return false;
      }
    }
    return true;
  }

  // 存储数据
  trackEvents({ properties = {}, ...val }, send_flag = false) {
    let flag = this.#getEventExist({ properties, ...val });
    if (!flag) return;
    let data = {
      properties: { ...this.#devices, ...properties },
      ...val,
      $zz_metadata: {
        // 事件会话id 每次改变
        $zz_event_id: getUUID(),
        // 小程序启动唯一id
        $zz_launching_id: this.#zz_launching_id,
        // 排序
        $zz_session_seq_id: this.#eventList.length ? this.#eventList.length : 0,
        // 时间戳
        $zz_session_start_sec: Date.now(),
        // 每50条更换
        $zz_session_id: this.#zz_session_id,
      },
    };
    this.#eventList.push(data);
    console.log("%c 🎂", "color:#33a5ff", this.#eventList);
    if (this.#eventList.length >= 50 || send_flag) {
      let list = JSON.parse(JSON.stringify(this.#eventList));
      let resData = this.#RSA.encryptLong(JSON.stringify(list));
      this.#eventList = [];
      // 重置每50条会话id
      this.#zz_session_id = getUUID();
      this.send(resData).then((res) => {
        // console.log("发送成功");
      });
    }
  }
  // 定时上报事件
  #intervalSend() {
    if (this.#intervalTim) clearInterval(this.#intervalTim);
    this.#intervalTim = setInterval(() => {
      if (this.#eventList.length > 0) {
        let list = JSON.parse(JSON.stringify(this.#eventList));
        let resData = this.#RSA.encryptLong(JSON.stringify(list));
        this.#eventList = [];
        // 重置每50条会话id
        this.#zz_session_id = getUUID();
        this.send(resData);
      }
    }, 6 * 10 * 1000);
  }
  // 获取应用详情
  #getAppDetail(data) {
    let that = this;
    return new Promise(async (resolve, reject) => {
      try {
        let {
          data: { events, pages, superProperties },
        } = await that.#requset.post({
          url: "/collect/getTrackingConfInfo",
          header: { appId: that.#key },
        });
        that.#customEvenList = events;
        that.#pageList = pages;
        that.#superProperties = superProperties;
        let {
          data: { privateKey, publicKey },
        } = await that.#requset.post({
          url: "/platform/detail",
          data,
        });

        that.#RSA.setPublicKey(publicKey);
        that.#RSA.setPrivateKey(privateKey);

        // console.log("%c 🎂", "color:#33a5ff", events);
        // console.log("%c 🎂", "color:#33a5ff", pages);
        // console.log("%c 🎂", "color:#33a5ff", superProperties);
        resolve();
      } catch (err) {
        reject(err);
      }
    });
  }

  // 上报
  send(data) {
    return new Promise((resolve, reject) => {
      this.#requset
        .post({
          url: "/collect/trackingCollect",
          data: {
            reqParams: data,
            appId: this.#key,
          },
          header: {
            appId: this.#key,
          },
        })
        .then((res) => {
          resolve(res);
        })
        .catch((err) => {
          reject(err);
        });
    });
  }

  // 首次加载
  #onLaunch() {
    // console.log("首次加载");
    let event = "$ae_app_open";
    this.trackEvents({
      event,
    });
  }
  // 显示
  #onShow() {
    this.#launchFre = 1;
    // console.log("显示页面");
    let event = "$ae_app_start";
    this.#pageTimer = Date.now();
    this.trackEvents({
      event,
    });
  }
  // 隐藏
  #onHide() {
    // console.log("隐藏页面");
    let event = "$ae_app_terminate";
    this.#pageTimerEnd = Date.now();
    let $ae_session_length = this.#calcTim(this.#pageTimer, this.#pageTimerEnd);
    this.trackEvents(
      {
        properties: { $ae_session_length },
        event,
      },
      true
    );
  }
  // 为page内部的show和hide和unload增加方法并执行原有的方法
  async #setPage() {
    let that = this;
    let oldPage = Page;
    Page = function (page) {
      if (!page) {
        page = {};
      }
      // let onOldSHow = page["onShow"];
      // let onOldUnload = page["onUnload"];
      // let onOldHide = page["onHide"];
      let _getPageInc = function () {
        const pageObj = getCurrentPages();
        const currentPage = pageObj[pageObj.length - 1];
        if (
          that.#pageList.findIndex(
            (i) => i.page_code == currentPage.data?.page_code
          ) != -1
        ) {
          return true;
        } else {
          return false;
        }
      };

      // 旧方法...
      let _types = {
        data: 1,
        onLoad: 1,
        onShow: 1,
        onReady: 1,
        onPullDownRefresh: 1,
        onShareAppMessage: 1,
        onShareTimeline: 1,
        onReachBottom: 1,
        onPageScroll: 1,
        onResize: 1,
        onTabItemTap: 1,
        onHide: 1,
        onUnload: 1,
      };
      // 存储当前页的所有方法
      let _methods = [];
      for (let m in page) {
        if (typeof page[m] === "function" && !_types[m]) {
          _methods.push(m);
        }
      }
      // 循环重写旧方法
      for (let i = 0; i < _methods.length; i++) {
        let oldFunc = page[_methods[i]];
        page[_methods[i]] = function () {
          oldFunc.apply(this, arguments);
        };
      }
      // 公用函数
      page["_sendAppView"] = function () {
        let pageObj = getCurrentPages();
        const currentPage = pageObj[pageObj.length - 1];
        const prePage = pageObj[pageObj.length - 2];
        const page_stay_time = (Date.now() - that.#start_time) / 1000;
        if (page_stay_time > that.#screen_time) {
          let pageSendData = {
            properties: {
              $ae_session_length: page_stay_time,
              $ae_page_code: currentPage.data?.page_code,
              $ae_last_page_code: prePage?.data?.page_code,
            },
            event: "$ae_appview_screen",
          };
          that.trackEvents(pageSendData);
        }
      };
      //在onshow生命周期开始计时
      let _show = function (options) {
        if (_getPageInc) {
          that.#start_time = Date.now();
        }
      };
      //在onHide生命周期停止计时
      let _hide = function (options) {
        if (_getPageInc) {
          page["_sendAppView"]();
        }
        that.removeElementExposure();
      };
      //在onUnload生命周期停止计时
      let _unload = function (options) {
        if (_getPageInc) {
          page["_sendAppView"]();
        }
        that.removeElementExposure();
      };
      // 对方法进行执行操作
      that.#mpProxy(page, "onShow", _show);
      that.#mpProxy(page, "onHide", _hide);
      that.#mpProxy(page, "onUnload", _unload);
      // 返回页面并执行
      return oldPage.apply(this, arguments);
    };
  }
  #mpProxy(option, method, newfn) {
    // 自己的方法后执行
    let newFunc = newfn;
    if (option[method]) {
      // 系统自带的方法先执行
      let oldFunc = option[method];
      option[method] = function () {
        // 旧方法和新方法
        oldFunc.apply(this, arguments);
        newFunc.apply(this, arguments);
      };
    } else {
      option[method] = function () {
        newFunc.apply(this, arguments);
      };
    }
  }

  // 设置用户信息
  setUserInfo(val) {
    this.setDevices({
      $distinct_id: val,
    });
    this.trackEvents({
      event: "$identify",
    });
  }
  // 元素曝光度监听并上报
  elementExposure(config = {}) {
    const that = this;
    config = { ...this.#exposureConfig, ...config };
    if (!config.ids.length) return false;
    let cur = getCurrentPages()[getCurrentPages().length - 1];
    for (const k of config.ids) {
      let obj = {
        key: k,
      };
      obj["eleObserve"] = wx.createIntersectionObserver(cur, {
        thresholds: [config.rate],
      });
      obj["eleObserve"].relativeToViewport().observe("#" + k, (res) => {
        if (res.intersectionRatio > config.rate) {
          obj.eleTim = setTimeout(() => {
            clearTimeout(obj.eleTim);
            that.trackEvents({
              event: config.event,
              properties: config.properties,
            });
          }, config.duration * 1000);
        } else {
          clearTimeout(obj.eleTim);
        }
      });
      that.#elementList.push(obj);
    }
  }
  // 移除监听
  removeElementExposure() {
    if (this.#elementList.length > 0) {
      for (let index = 0; index < this.#elementList.length; index++) {
        const element = this.#elementList[index];
        element.eleObserve.disconnect();
        clearTimeout(element.eleTim);
        element.eleTim = null;
      }
      this.#elementList = [];
    }
  }
  // 深克隆 用不上了
  #deepClone(obj) {
    if (obj === null || typeof obj !== "object") {
      return obj;
    }
    let clone = Array.isArray(obj) ? [] : {};
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        clone[key] = deepClone(obj[key]);
      }
    }
    return clone;
  }
}
